package com.ytktt.study.week2.task6;

import org.apache.http.*;
import org.apache.http.client.HttpRequestRetryHandler;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.conn.ConnectTimeoutException;
import org.apache.http.conn.ConnectionKeepAliveStrategy;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.DefaultConnectionKeepAliveStrategy;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.protocol.HttpContext;
import org.apache.http.util.EntityUtils;
import org.jetbrains.annotations.NotNull;

import javax.net.ssl.SSLException;
import java.io.InterruptedIOException;
import java.net.UnknownHostException;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * HttpClient4 请求 demo
 * 可能是目前http client使用的最广泛的版本，springboot基础支持
 * 更多参考 https://hc.apache.org/httpcomponents-client-4.5.x/current/tutorial/html/index.html
 * 可以很多参数以及请求地址可以做成可配置的，本例主要作练习用。
 *
 * @author ytk
 */
public class HttpClient4Demo {

    public static void main(String[] args) throws Exception {
        try (CloseableHttpClient httpClient = HttpClients.custom()
                // 自定义 keepalive 策略
                .setKeepAliveStrategy(getConnectionKeepAliveStrategy())
                // 添加自定义请求重试处理器
                .setRetryHandler(getHttpRequestRetryHandler())
                // 设置连接管理器
                .setConnectionManager(getPoolingHttpClientConnectionManager())
                // 添加自定义请求拦截器
                .addInterceptorLast(getHttpRequestInterceptor())
                .build()) {
            RequestConfig requestConfig = RequestConfig.custom()
                    // 设置 socket 超时时间（请求服务器传输数据的事件）
                    .setSocketTimeout(3_000)
                    // 设置连接超时时间（请求服务器发起建立连接的时间）
                    .setConnectTimeout(3_000)
                    .build();
            HttpGet httpGet = new HttpGet("http://localhost:8081");
            httpGet.setConfig(requestConfig);

            HttpClientContext localContext = HttpClientContext.create();
            localContext.setAttribute("requestCount", new AtomicInteger(1));

            try (CloseableHttpResponse response = httpClient.execute(httpGet, localContext)) {
                StatusLine statusLine = response.getStatusLine();
                if (HttpStatus.SC_OK == statusLine.getStatusCode()) {
                    HttpEntity entity = response.getEntity();
                    String contentType = entity.getContentType().getElements()[0].getName();
                    if (contentType.contains("text/html")) {
                        String content = EntityUtils.toString(entity, StandardCharsets.UTF_8);
                        System.out.println(content);
                    }
                    // 处理些其他的格式例如 application/json
                } else {
                    System.out.println("请求发生错误: " + statusLine.getStatusCode());
                }
            }
        }
    }

    @NotNull
    private static ConnectionKeepAliveStrategy getConnectionKeepAliveStrategy() {
        return new DefaultConnectionKeepAliveStrategy() {
            @Override
            public long getKeepAliveDuration(HttpResponse response, HttpContext context) {
                long keepAlive = super.getKeepAliveDuration(response, context);
                // 如果服务端没有设置keepalive
                if (keepAlive == -1) {
                    // 设置 keepalive时间为3s
                    keepAlive = 3_000L;
                }
                return keepAlive;
            }
        };
    }

    @NotNull
    private static HttpRequestRetryHandler getHttpRequestRetryHandler() {
        return (exception, executionCount, context) -> {
            if (executionCount >= 5) {
                // 重试次数不能大于等于5次
                return false;
            }
            if (exception instanceof InterruptedIOException) {
                // IO 中断不再尝试
                return false;
            }
            if (exception instanceof UnknownHostException) {
                // 找不到域名 不再尝试
                return false;
            }
            if (exception instanceof ConnectTimeoutException) {
                // 连接超时再次尝试
                return true;
            }
            if (exception instanceof SSLException) {
                // SSL 握手失败再次重试
                return true;
            }
            HttpClientContext clientContext = HttpClientContext.adapt(context);
            HttpRequest request = clientContext.getRequest();
            boolean idempotent = true;
            if (idempotent) {
                // 可以根据request判断请求是否是幂等的，例如get/delete/put方法
                return true;
            }
            return false;
        };
    }

    @NotNull
    private static PoolingHttpClientConnectionManager getPoolingHttpClientConnectionManager() {
        // 池化管理连接
        PoolingHttpClientConnectionManager manager = new PoolingHttpClientConnectionManager();
        // 设置连接池的最大连接数 默认 20
        manager.setMaxTotal(200);
        // 每个请求最多的并发量 默认2
        manager.setDefaultMaxPerRoute(32);
        return manager;
    }

    @NotNull
    private static HttpRequestInterceptor getHttpRequestInterceptor() {
        return (httpRequest, httpContext) -> {
            AtomicInteger count = (AtomicInteger) httpContext.getAttribute("requestCount");
            // 请求计数
            httpRequest.addHeader("X-Request-Count", Integer.toString(count.getAndIncrement()));
            // 还可以记录一些日志...
        };
    }
}
